package com.wwj.server.game;

import com.google.gson.GsonBuilder;
import com.wwj.server.codc.MessageDecoder;
import com.wwj.server.codc.MessageEncoder;
import com.wwj.server.game.msg.GameServerBindMsg;
import com.wwj.server.game.msg.GameServerHeartbeatMsg;
import com.wwj.server.model.Message;
import com.wwj.server.model.NetBaseMsg;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.ConnectException;
import java.net.InetSocketAddress;

/**
 * netty客户端入门
 *
 * @author tysci
 */
@Component
public class Client {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String hostname;

    private int port;

    private int reconnectTime;

    private int rebindTime;

    private String serverName;


    private boolean isRegister = false;

    private static boolean flag = true;

    /**
     * 服务类
     */
    Bootstrap bootstrap = new Bootstrap();

    /**
     * 会话
     */
    private Channel channel;

    /**
     * 线程池
     */
    private EventLoopGroup workerGroup = new NioEventLoopGroup();

    /**
     * 初始化
     */
    @PostConstruct
    public void init() {

        // 设置循环线程组事例
        bootstrap.group(workerGroup);

        // 设置channel工厂
        bootstrap.channel(NioSocketChannel.class);

        // 设置管道
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new MessageDecoder());
                ch.pipeline().addLast(new MessageEncoder());
                ch.pipeline().addLast(new ClientHandler());
            }
        });
    }

    /**
     * 连接
     *
     * @throws InterruptedException
     */
    public void connect(String hostname, int port, int reconnectTime) {

        this.hostname = hostname;
        this.port = port;
        this.reconnectTime = reconnectTime;

        boolean flag = true;
        while (flag) {
            try {
                //game服与control服建立连接
                // 连接服务端
                ChannelFuture connect = bootstrap.connect(new InetSocketAddress(hostname, port));
                connect.sync();
                channel = connect.channel();
                flag = false;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (Exception e) {
                if (e instanceof ConnectException) {
                    logger.warn("ControlServer={}未启动,{}s后重新尝试连接", hostname, reconnectTime / 1000);
                    try {
                        Thread.sleep(reconnectTime);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }
        logger.info("GameServer客户端已启动,远程IP:{},端口:{}", hostname, port);
    }

    public void bind(String serverName, int rebindTime) {
        this.serverName = serverName;
        this.rebindTime = rebindTime;

        //game服与control绑定注册
        while (!isRegister) {
            GameServerBindMsg msg = new GameServerBindMsg(serverName);
            sendMessage(msg);
            try {
                Thread.sleep(rebindTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        logger.info("GameServer「ServerName={}」 <--> ControlServer「HostName={}」 远程连接已建立", serverName, hostname);
    }

    public Thread heartbeat() {
        final String serverName = this.serverName;
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                Client.flag = false;
            }
        });
        return new Thread(new Runnable() {
            @Override
            public void run() {
                logger.info("GameServer「ServerName={}」心跳线程启动...", serverName);
                while (flag) {
                    sendMessage(new GameServerHeartbeatMsg(serverName));
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        logger.error("GameServer心跳线程:", e);
                    }
                }
            }
        });
    }

    public void rebind() {
        bind(this.serverName, this.rebindTime);
    }

    /**
     * 关闭
     */
    public void shutdown() {
        workerGroup.shutdownGracefully();
    }

    /**
     * 获取会话
     *
     * @return
     */
    public Channel getChannel() {
        return channel;
    }

    /**
     * 发送消息
     *
     * @param message
     * @throws ConnectException,InterruptedException
     */
    public void sendMessage(Message message) throws ConnectException, InterruptedException {
        if (channel == null || !channel.isActive()) {
            connect(this.hostname, this.port, this.reconnectTime);
        }
        channel.writeAndFlush(message);
    }

    public <T extends NetBaseMsg> void sendMessage(T message) {
        if (channel == null || !channel.isActive()) {
            connect(this.hostname, this.port, this.reconnectTime);
        }
        Message msg = new Message(new GsonBuilder()
                .disableHtmlEscaping()
                .serializeNulls().create().toJson(message).getBytes());
        channel.writeAndFlush(msg);
    }

    public boolean isRegister() {
        return isRegister;
    }

    public void setRegister(boolean register) {
        isRegister = register;
    }

    public String getServerName() {
        return serverName;
    }
}
